/**********************************************************************
*  Copyright (c) 2007 - Victor Jacobs - victor.jacobs@gmail.com
*
*  Permission is hereby granted, free of charge, to any person
*  obtaining a copy of this software and associated documentation
*  files (the "Software"), to deal in the Software without
*  restriction, including without limitation the rights to use,
*  copy, modify, merge, publish, distribute, sublicense, and/or sell
*  copies of the Software, and to permit persons to whom the
*  Software is furnished to do so, subject to the following
*  conditions:
*
*  The above copyright notice and this permission notice shall be
*  included in all copies or substantial portions of the Software.
*
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
*  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
*  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
*  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
*  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
*  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
*  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
*  OTHER DEALINGS IN THE SOFTWARE.
**********************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using Vj.MathLibrary;
using System.Collections.Specialized;
using System.IO;

namespace Vj.ShapeLibrary.ObjConverter
{
    public class ObjReader
    {
        public static List<ObjModel> ReadFile(string fileName)
        {
            if (!File.Exists(fileName))
                throw new FileNotFoundException();

            // add try/catch here
            FileStream fs = new FileStream(fileName, FileMode.Open);
            TextReader tr = new StreamReader(fs);

            string fileContents = tr.ReadToEnd();

            tr.Close();
            fs.Close();

            return Read(fileContents);
        }

        public static List<ObjModel> Read(string objFileContents)
        {
            char[] delimiterChars = { '\r', '\n' };
            string [] allStatementsArray = objFileContents.Split(delimiterChars);

            List<StringCollection> objStatementsList = new List<StringCollection>();
            
            StringCollection objectStatements = new StringCollection();

            foreach (string currentStatement in allStatementsArray)
            {
                if (currentStatement.ToLower().Trim().StartsWith("o"))
                {
                    if (objectStatements.Count != 0)
                        objStatementsList.Add(objectStatements);

                    objectStatements = new StringCollection();
                    objectStatements.Add(currentStatement);
                }
                else if (currentStatement.ToLower().Trim().StartsWith("v") ||
                        currentStatement.ToLower().Trim().StartsWith("vn") ||
                        currentStatement.ToLower().Trim().StartsWith("vt") ||
                        currentStatement.ToLower().Trim().StartsWith("f"))
                {
                    objectStatements.Add(currentStatement);
                }
            }

            objStatementsList.Add(objectStatements);

             // read through file and get all blocks between objects
            List<ObjModel> objModelList = new List<ObjModel>();

            foreach (StringCollection statementList in objStatementsList)
                objModelList.Add(ParseModel(statementList));

            return objModelList;
        }

        private static ObjModel ParseModel(StringCollection statements)
        {
            ObjModel objModel = new ObjModel();

            foreach (string statement in statements)
            {
                if (statement.Trim().ToLower().StartsWith("o"))
                {
                    objModel.Name = statement.Substring(1, statement.Length - 1).Trim().ToLower();
                }
                else if (statement.Trim().ToLower().StartsWith("vn"))
                {
                    objModel.NormalList.Add(ParseVector(statement));
                }
                else if (statement.Trim().ToLower().StartsWith("v"))
                {
                    objModel.VertexList.Add(ParseVector(statement));
                }
                else if (statement.Trim().ToLower().StartsWith("f"))
                {
                    objModel.ShapeList.Add(ParseShape(statement, objModel));
                }
            }

            return objModel;
        }

        private static Vector ParseVector(string statement)
        {
            int firstValueIndex = statement.IndexOf(" ", 0);
            int secondValueIndex = statement.IndexOf(" ", firstValueIndex + 1);
            int thirdValueIndex = statement.IndexOf(" ", secondValueIndex + 1);

            double xValue;
            double yValue;
            double zValue;

            double.TryParse((statement.Substring(firstValueIndex, secondValueIndex - firstValueIndex)).Trim(), out xValue);
            double.TryParse((statement.Substring(secondValueIndex, thirdValueIndex - secondValueIndex)).Trim(), out yValue);
            double.TryParse((statement.Substring(thirdValueIndex, statement.Length - thirdValueIndex)).Trim(), out zValue);

            return new Vector(xValue, yValue, zValue);
        }

        private static ObjShape ParseShape(string statement, ObjModel objModel)
        {
            return ParseShape(statement, objModel, 0, 0);
        }

        private static ObjShape ParseShape(string statement, ObjModel objModel, int vertexOffset, int normalOffset)
        {
            int firstIndex = statement.IndexOf(" ", 0);
            int secondIndex = statement.IndexOf(" ", firstIndex + 1);
            int thirdIndex = statement.IndexOf(" ", secondIndex + 1);
            int fourthIndex = statement.IndexOf(" ", thirdIndex + 1);

            bool quadObject = false;

            if (fourthIndex > thirdIndex)
                quadObject = true;

            string firstTriplet = statement.Substring(firstIndex, secondIndex - firstIndex).Trim();
            string secondTriplet = statement.Substring(secondIndex, thirdIndex - secondIndex).Trim();
            string thirdTriplet = statement.Substring(thirdIndex, statement.Length - thirdIndex).Trim();

            string fourthTriplet = "";
            if (quadObject)
                fourthTriplet = statement.Substring(fourthIndex, statement.Length - fourthIndex).Trim();

            Vector[] vertexSet = null;
            Vector normal = null;

            try
            {
                if (quadObject)
                {
                    vertexSet = new Vector[4];
                    vertexSet[0] = objModel.VertexList[int.Parse(firstTriplet.Substring(0, firstTriplet.IndexOf("/"))) - 1];
                    vertexSet[1] = objModel.VertexList[int.Parse(secondTriplet.Substring(0, secondTriplet.IndexOf("/"))) - 1];
                    vertexSet[2] = objModel.VertexList[int.Parse(thirdTriplet.Substring(0, thirdTriplet.IndexOf("/"))) - 1];
                    vertexSet[3] = objModel.VertexList[int.Parse(fourthTriplet.Substring(0, fourthTriplet.IndexOf("/"))) - 1];

                    normal = new Vector();
                    normal = objModel.NormalList[int.Parse(firstTriplet.Substring(firstTriplet.LastIndexOf("/") + 1, firstTriplet.Length - (firstTriplet.LastIndexOf("/") + 1))) - 1];
                }
                else
                {
                    vertexSet = new Vector[3];
                    vertexSet[0] = objModel.VertexList[int.Parse(firstTriplet.Substring(0, firstTriplet.IndexOf("/"))) - 1];
                    vertexSet[1] = objModel.VertexList[int.Parse(secondTriplet.Substring(0, secondTriplet.IndexOf("/"))) - 1];
                    vertexSet[2] = objModel.VertexList[int.Parse(thirdTriplet.Substring(0, thirdTriplet.IndexOf("/"))) - 1];

                    normal = new Vector();
                    normal = objModel.NormalList[int.Parse(firstTriplet.Substring(firstTriplet.LastIndexOf("/") + 1, firstTriplet.Length - (firstTriplet.LastIndexOf("/") + 1))) - 1];
                }

            }
            catch (Exception ex)
            { 
                // need to add functionality to support reading in multiple objects
                // - or only allow single object import

                // REASON: review how obj files list faces for mutiple object files
            }

            return new ObjShape(vertexSet, normal, null);
        }

    }
}
